<script setup lang="ts">
import {nextTick, onMounted, ref, Ref} from 'vue'
import {Command, type SpawnOptions} from '@tauri-apps/plugin-shell'
import {invoke} from '@tauri-apps/api/core'
import {homeDir} from '@tauri-apps/api/path'

interface Props {
  width?: string | number
  height?: string | number
}

const props = withDefaults(defineProps<Props>(), {
  width: '100%',
  height: '100%'
})

interface CommandHistory {
  command: string
  type: 'column' | 'line' | 'table'
  output: string | string[] | string[][]
  header?: string[]  // 表格表头
  timestamp: Date
  cwd?: string
}

// 设置命令行输入框
const commandInput = ref('')
// 设置终端输出
const terminalOutput = ref<HTMLElement | null>(null)
// 设置命令历史记录
const commandHistory: Ref<CommandHistory[]> = ref([])
// 设置命令行输入框
const inlineInput = ref<HTMLInputElement | null>(null)
// 设置当前工作目录
const currentDirectory = ref<string>('')
// 设置用户目录
const userHomeDirectory = ref<string>('')
// 设置是否是Windows系统
const isWindows = typeof navigator !== 'undefined' && navigator.userAgent.includes('Windows')
// 设置shell命令名称和参数前缀
const shellCommandName = isWindows ? 'windows-shell' : 'unix-shell'
// 设置工作目录命令
const shellArgsPrefix = isWindows ? ['/c'] : ['-c']
// 设置工作目录命令
const workingDirectoryCommand = isWindows ? 'cd' : 'pwd'
// 记录终端命令运行需要的环境变量
const commandEnv = ref<Record<string, string> | undefined>()

/**
 * @function buildCommandOptions
 * @description 构建命令执行需要的选项
 * @param cwd 当前工作目录
 */
const buildCommandOptions = (cwd?: string): SpawnOptions | undefined => {
  const options: SpawnOptions = {}
  if (cwd) {
    options.cwd = cwd
  }
  if (commandEnv.value) {
    options.env = commandEnv.value
  }
  return Object.keys(options).length ? options : undefined
}

/**
 * @function loadCommandEnv
 * @description 获取登录 shell 的 PATH 信息
 */
const loadCommandEnv = async () => {
  try {
    const loginShellPath = await invoke<string>('get_login_shell_path')
    commandEnv.value = { PATH: loginShellPath }
  } catch (error) {
    console.warn('Failed to load login shell PATH', error)
    commandEnv.value = undefined
  }
}

// 历史命令相关
const commandHistoryList = ref<string[]>([])  // 只存储命令文本的历史记录
const historyIndex = ref<number>(-1)  // 当前历史索引，-1 表示不在历史记录中
const tempInput = ref<string>('')  // 临时保存当前输入，用于下键时恢复

/**
 * @function scrollToBottom
 * @description 滚动到底部
 */
const scrollToBottom = () => {
  const scroll = () => {
    if (terminalOutput.value) {
      // 强制重新计算元素尺寸
      const element = terminalOutput.value;
      element.style.overflowY = 'scroll';

      // 获取最新的尺寸信息
      const { scrollHeight } = element;

      // 即使maxScrollTop为0，也尝试滚动到底部
      element.scrollTop = scrollHeight;

      // 如果还是不行，使用scrollIntoView
      const lastEntry = element.querySelector('.terminal-entry:last-child');
      if (lastEntry) {
        lastEntry.scrollIntoView({ behavior: 'smooth', block: 'end' });
        console.log("使用scrollIntoView滚动");
      }
    }
  };

  // 在DOM更新后执行
  nextTick(() => {
    // 等待内容渲染完成
    setTimeout(scroll, 10);
    // 再等一会儿确保完成
    setTimeout(scroll, 100);
  });
}

/**
 * @function focusCommandInput
 * @description 聚焦命令行输入框
 */
const focusCommandInput = () => {
  setTimeout(() => {
    inlineInput.value?.focus();
  }, 0);
}

/**
 * @function handleHistoryNavigation
 * @description 处理历史命令导航
 * @param direction 方向
 */
const handleHistoryNavigation = (direction: 'up' | 'down') => {
  if (commandHistoryList.value.length === 0) return

  if (direction === 'up') {
    // 上键：显示上一个历史命令
    if (historyIndex.value === -1) {
      // 首次按上键，保存当前输入
      tempInput.value = commandInput.value
      historyIndex.value = commandHistoryList.value.length - 1
    } else if (historyIndex.value > 0) {
      historyIndex.value--
    }
    commandInput.value = commandHistoryList.value[historyIndex.value]
  } else {
    // 下键：显示下一个历史命令
    if (historyIndex.value === -1) {
      // 不在历史记录中，不处理
      return
    } else if (historyIndex.value < commandHistoryList.value.length - 1) {
      historyIndex.value++
      commandInput.value = commandHistoryList.value[historyIndex.value]
    } else {
      // 到达历史记录末尾，恢复临时输入
      historyIndex.value = -1
      commandInput.value = tempInput.value
      tempInput.value = ''
    }
  }
}

/**
 * @function executeCommand
 * @description 执行命令
 */
const executeCommand = async () => {
  if (!commandInput.value.trim()) return

  const commandText = commandInput.value.trim()

  // 添加到历史命令列表（避免重复添加相同的命令）
  if (commandHistoryList.value.length === 0 ||
      commandHistoryList.value[commandHistoryList.value.length - 1] !== commandText) {
    commandHistoryList.value.push(commandText)
  }

  // 重置历史索引
  historyIndex.value = -1
  tempInput.value = ''
  commandInput.value = ''

  const options = buildCommandOptions(currentDirectory.value || undefined)
  try {
    if (commandText.toLowerCase().startsWith('cd')) {
      await handleDirectoryChange(commandText, options)
      // cd命令后也需要滚动
      setTimeout(() => scrollToBottom(), 10);
      return
    }

    // 使用系统 shell 执行命令，允许复合命令与管道
    const command = Command.create(shellCommandName, [...shellArgsPrefix, commandText], options)
    const output = await command.execute()

    console.log("output",output)

    const {type, header, data} = await formatCommand(output.stdout, output.stderr)
    console.log("type", type);
    console.log("data",  data);
    console.log("header", header);

    // 将结果添加到历史记录中（命令失败时显示错误信息）
    const outputData = output.code !== 0 && !data ? [`Command exited with code ${output.code}`] : (data || [])

    commandHistory.value.push({
      command: commandText,
      type: type,
      output: outputData,
      header: header,
      timestamp: new Date(),
      cwd: currentDirectory.value || userHomeDirectory.value || '~'
    })
  } catch (error) {
    commandHistory.value.push({
      command: commandText,
      type: 'line',
      output: [`Error: ${(error as Error).message}`],
      timestamp: new Date()
    })
  }
  // 滚动到底部
  setTimeout(() => scrollToBottom(), 10);
}


/**
 * @function formatCommand
 * @description 格式化命令输出, 根据命令输出内容决定返回类型
 * @param stdout 命令输出
 * @param stderr 错误信息
 */
const formatCommand = async (stdout: string, stderr: string): Promise<{type: 'column' | 'line' | 'table', data: string[] | string[][], header?: string[]}> => {

  // 如果stdout为空，则返回错误信息
  if ( stdout === "" && stderr !== "") {
    return {type: 'line', data: [stderr], header: []}
  }

  // 分割成行
  const lines = stdout.trim().split('\n');

  // 如果只有一行，则返回行格式
  if (lines.length === 1) {
    return {type: 'line', data: lines, header: []}
  }

  // 设定header
  let header: string[] = [];
  // 设定表格data
  const tableData: string[][] = [];

  // 判断第一行的单词是否是大写
  const firstWord = lines[0].trim().split(/\s{2,}/)[0];
  if (firstWord.toUpperCase() === firstWord) {
    // 提取header
    lines[0].trim().split(/\s{1,}/).forEach(line => {
      if (line === '.' || line === '..') {
        return
      }
      let item = line.split("\t").map(h => h.trim());
      if (header.length > 1) {
        header.push(...item);
      }else{
        header.push(item[0]);
      }
    });
  }

  // 如果header只有一行, 判断是否单词, 还是语句
  // 如果等于false说明是单词, 否则是语句
  if (header.length === 0) {
    const isWord = lines.some(line => line.trim().split(/\s+/).length > 2)
    console.log("单词判断",isWord)
    // 判断语句是否都有等号
    if (!isWord) {
      const allHaveEquals = lines.every((item:string) => item.includes('='));
      if (allHaveEquals) {
        return {type: 'column', data: lines, header: header}
      }
    }

    return isWord ? {type: 'column', data: lines, header: header} : {type: 'line', data: lines, header: header}
  }

  if (header.length === 1) {
    for (let i = 1; i < lines.length; i++) {
      const line = lines[i].trim();
      tableData.push([line])
    }
    // 将lines的第一个元素作为header, 剩余的作为data
    return {type: 'table', data: tableData, header: header}
  }

  // 如果header有多行，则返回表格格式
  for (let i = 1; i < lines.length; i++) {
    const line = lines[i].trim();
    if (!line) continue;
    // 使用正则精确提取各字段
    const regex = /^(\S+)\s+(\S+)\s+(\d+)\s+([\d\-]+\s[\d:.]+\s[+\-\d]+\s\w+)\s+(\S+)\s+(\S+)\s+(.+)$/;
    const match = line.match(regex);
    // 循环添加row数据
    const row: string[] = [];
    if (match) {
      for (let i = 0; i < header.length; i++) {
        row.push(match[i + 1]);
      }
    }else{
      const a = lines[i].split(/\s+/);
      row.push(...a)
    }
    tableData.push(row);

  }
  return {type: 'table', data: tableData, header: header}
}

/**
 * @function extractLastLine
 * @description 提取最后一行
 * @param text 文本
 */
const extractLastLine = async (text: string) => {
  const lines = text.split('\n').map(line => line.trim()).filter(Boolean)
  return lines[lines.length - 1] || ''
}

/**
 * @function handleDirectoryChange
 * @description 处理目录变更
 * @param commandText 命令文本
 * @param options 选项
 */
const handleDirectoryChange = async (commandText: string, options?: SpawnOptions)  => {
  const target = commandText.replace(/^cd/i, '').trim()
  const resolvedTarget = await resolveTargetDirectory(target)
  const safeTarget = escapeForDoubleQuotes(resolvedTarget)
  const cdCommand = resolvedTarget
      ? `cd "${safeTarget}" && ${workingDirectoryCommand}`
      : workingDirectoryCommand
  const command = Command.create(shellCommandName, [...shellArgsPrefix, cdCommand], options)
  const output = await command.execute()

  const combinedOutput = [output.stdout, output.stderr].filter(Boolean).join('\n')
  const parsedDirectory = await extractLastLine(combinedOutput)

  if (output.code === 0 && parsedDirectory) {
    currentDirectory.value = parsedDirectory
  }

  // 格式化输出：将多行输出转换为数组，每行作为一个元素
  const outputText = combinedOutput || (output.code === 0 ? parsedDirectory || 'Directory unchanged' : `Command exited with code ${output.code}`)
  const outputLines = outputText.split('\n')
      .map(line => line.trim())
      .filter(line => line.length > 0)

  // 如果没有输出行，至少显示一个空字符串或状态信息
  const formattedOutput = outputLines.length > 0 ? outputLines : [outputText]

  commandHistory.value.push({
    command: commandText,
    type: 'column',
    output: formattedOutput,
    timestamp: new Date(),
    cwd: currentDirectory.value || userHomeDirectory.value || '~'
  })
}

/**
 * @function initializeDirectory
 * @description 初始化目录
 */
const initializeDirectory = async () => {
  try {
    const options = buildCommandOptions()
    const command = Command.create(shellCommandName, [...shellArgsPrefix, workingDirectoryCommand], options)
    const output = await command.execute()
    const parsedDirectory = await extractLastLine(output.stdout || output.stderr)
    if (parsedDirectory) {
      currentDirectory.value = parsedDirectory
    }
  } catch (error) {
    console.error('Failed to read initial directory', error)
  }
}

/**
 * @function resolveTargetDirectory
 * @description 解析目标目录
 * @param target 目标目录
 */
const resolveTargetDirectory = async (target: string)=> {
  if (!target) return ''
  if (target === '~' || target.startsWith('~/')) {
    userHomeDirectory.value = await homeDir()
    const home = userHomeDirectory.value
    const normalizedHome = home.endsWith('/') ? home.slice(0, -1) : home
    return `${normalizedHome}${target.slice(1)}`
  }
  if (target === '-') {
    return currentDirectory.value || ''
  }
  return target
}

/**
 * @function escapeForDoubleQuotes
 * @description 转义双引号
 * @param value 值
 */
const escapeForDoubleQuotes = (value: string) => {
  return value.replace(/(["\\$`])/g, '\\$1')
}

/**
 * @function onMounted
 * @description 组件挂载
 */
onMounted(async () => {
  await loadCommandEnv()
  await initializeDirectory()
  // 延迟执行确保初始化完成后再滚动
  setTimeout(() => {
    scrollToBottom()
    focusCommandInput()
  }, 100)
})
</script>

<template>
  <div class="tarot-terminal" :style="{ width: props.width, height: props.height }">
    <div class="tarot-terminal-header">
      <slot name="header"></slot>
    </div>
    <div ref="terminalOutput" class="terminal-content" @click="focusCommandInput">
      <div v-for="(entry, index) in commandHistory" :key="index" class="terminal-entry">
        <div class="command-line">
          <span class="command-path">{{ entry.cwd || currentDirectory || '~' }}</span>
          <span class="command-prompt">$</span>
          <span class="command-text">{{ entry.command }}</span>
          <span class="command-timestamp">{{ entry.timestamp.toLocaleTimeString() }}</span>
        </div>

        <div class="command-content">
<!--          原始输出-->
<!--          <pre>{{ outData }}</pre>-->

          <ul class="command-content-column" v-if="entry.type === 'column'">
            <li class="command-content-column-text" v-for="(item, index) in entry.output as Array<string>" :key="index">
              <!-- 如果单词带有后缀, 则加粗显示 -->
              <span v-if="item==='.' || item==='..'" class="command-content-text-bold">{{ item }}</span>
              <span v-else-if="item.includes('.') || item.includes('..')">{{ item }}</span>
              <span v-else-if="item.split(' ').length > 1">{{ item }}</span>
              <span v-else class="command-content-text-bold">{{ item }}</span>
            </li>
          </ul>

          <ul class="command-content-line" v-if="entry.type === 'line'">
            <li class="command-content-line-text" v-for="(item, index) in entry.output as Array<string>" :key="index">
              <!-- 如果单词带有后缀, 则加粗显示 -->
              <span v-if="item==='.' || item==='..'" class="command-content-text-bold">{{ item }}</span>
              <span v-else-if="item.includes('.') || item.includes('..')">{{ item }}</span>
              <span v-else class="command-content-text-bold">{{ item }}</span>
            </li>
          </ul>
          <table class="command-content-table" v-if="entry.type === 'table'">
            <thead v-if="entry.header">
            <tr>
              <th v-for="(headerItem, headerIndex) in entry.header" :key="headerIndex">{{ headerItem }}</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="(row, rowIndex) in entry.output as any" :key="rowIndex">
              <td v-for="(item, colIndex) in row" :key="colIndex">{{ item }}</td>
            </tr>
            </tbody>
          </table>
        </div>
      </div>
      <div class="command-line">
        <span class="command-path">{{ currentDirectory || userHomeDirectory || '~' }}</span>
        <span class="command-prompt">$:</span>
        <input
            ref="inlineInput"
            v-model="commandInput"
            class="command-inline-input"
            placeholder="请输入命令..."
            autocapitalize="off"
            autocorrect="off"
            spellcheck="false"
            autocomplete="off"
            @keydown.enter.prevent="executeCommand"
            @keydown.up.prevent="handleHistoryNavigation('up')"
            @keydown.down.prevent="handleHistoryNavigation('down')"
        />
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.tarot-terminal {
  width: 100%;
  height: 100%;
  display: flex;
  flex: 1;
  flex-direction: column;
  overflow: hidden;
  background-color: var(--theme-bg-content);

  ul li {
    margin-left: 0!important;
  }

}
.tarot-terminal-header {
  display: flex;
}

.terminal-content {
  flex: 1;
  min-height: 0;
  width: 100%;
  padding: 8px;
  overflow-y: auto;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  &::-webkit-scrollbar {
    display: none;
  }
}


.terminal-output {
  flex: 1;
  padding: 0 8px;
  min-height: 0;
}


.terminal-entry {
  padding-bottom: 8px;

  .command-content {
    display: flex;
    color: var(--color-text-3);
    white-space: pre-wrap;
    margin: 12px 0 4px 16px;
    flex: 1;

    .command-content-text-bold {
      font-weight: bold;
      color: var(--theme-primary);
    }

    .command-content-column {
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      flex-wrap: wrap;
      gap: 10px 20px;

    }

    .command-content-line {
      flex: 1;
      display: flex;
      flex-direction: row;
      justify-content: flex-start;
      flex-wrap: wrap;
      gap: 10px 20px;
    }

    .command-content-table {
      border-collapse: collapse;
      width: 100%;
      table-layout: fixed;

      thead {
        tr {
          th {
            padding: 0 4px 8px 4px;
            text-align: left;
            font-weight: bold;
            color: var(--theme-primary);
            border-bottom: 1px solid var(--theme-primary);
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
          }
        }
      }

      tbody {
        tr {
          &:hover {
            background-color: var(--theme-10);
            color: var(--theme-primary);
          }

          td {
            padding: 2px;
            min-width: 0;
            border-bottom: 1px solid var(--theme-10);
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
          }
        }
      }
    }
  }
}

.command-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  padding: 8px 8px 8px;
  background-color: var(--theme-bg);

  .command-text {
    flex: 1;
    color: var(--theme-primary);
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .command-prompt {
    color: var(--theme-primary);
  }
  .command-path {
    color: var(--theme-primary);
    margin-right: 4px;
  }
  .command-timestamp {
    color: var(--theme-primary);
    font-size: 0.8rem;
    margin-left: auto;
  }

  .command-inline-input {
    flex: 1;
    background: transparent;
    border: none;
    color: var(--theme-primary);
    outline: none;
    caret-color: var(--theme-primary);
  }

  .command-inline-input::placeholder {
    color: var(--color-text-2);
  }
}
</style>